﻿
using Patterns4Net.Attributes;

namespace Patterns4Net.Tests.ArchitectureExplorer.Diagram
{
    using System.Collections.Generic;
    using System.Linq;
    using Patterns4Net.ArchitectureExplorer.Diagram;
    using Patterns4Net.ArchitectureExplorer.Diagram.Relations;
    using Attributes.Relations;

    /// <summary>
    /// Helper class for building of <see cref="Diagram"/> instances for testing purposes.
    /// </summary>
    public class DiagramBuilder
    {
        private readonly IList<LayerBuilder> layerBuilders = new List<LayerBuilder>();

        private readonly IList<Relation> relations = new List<Relation>();

        public LayerBuilder AddLayer(string name)
        {
            var result = new LayerBuilder(name);
            this.layerBuilders.Add(result);
            return result;
        }

        public Diagram Build()
        {
            var result = new Diagram();
            foreach (var layerBuilder in this.layerBuilders)
            {
                result.AddLayer(layerBuilder.Build());
            }
            return result;
        }

        public class LayerBuilder
        {
            private readonly string name;

            private IList<PackageBuilder> packageBuilders = new List<PackageBuilder>();

            public LayerBuilder(string name)
            {
                this.name = name;
            }            

            public PackageBuilder AddPackage(string packageName)
            {
                var result = new PackageBuilder(packageName);
                this.packageBuilders.Add(result);
                return result;
            }

            public Layer Build()
            {
                return new Layer(this.name, this.packageBuilders.Select(x => x.Build()).ToArray());
            }
        }

        public class PackageBuilder
        {
            private readonly string name;

            private readonly IList<TypeInfo> types = new List<TypeInfo>();

            public PackageBuilder(string name)
            {
                this.name = name;
            }

            public PackageBuilder Add(string typeName, AbstractionLevel level = AbstractionLevel.Normal)
            {
                var type = new TypeInfo(typeName, Enumerable.Empty<PropertyInfo>(), Enumerable.Empty<MethodInfo>());
                type.AbstractionLevel = level;
                this.types.Add(type);
                return this;
            }

            public Package Build()
            {
                return new Package(this.name, this.types);
            }
        }
    }

    public static class DiagramExtensions
    {
        public static Package GetPackage(this Diagram diagram, string name)
        {
            return diagram.Layers.SelectMany(x => x.Packages).First(p => p.Name == name);
        }

        public static void AddInheritance(this Diagram diagram, string source, string target)
        {
            diagram.AddRelation(
                new Inheritance(
                    diagram.Types.First(x => x.Name == source),
                    diagram.Types.First(x => x.Name == target)));
        }


        public static void AddComposition(this Diagram diagram, string source, string target, Cardinality cardinality = Cardinality.One)
        {
            diagram.AddRelation(
                new Composition(
                    diagram.Types.First(x => x.Name == source),
                    diagram.Types.First(x => x.Name == target), 
                    cardinality));
        }

        public static void AddBidirectionalComposition(this Diagram diagram, string source, string target)
        {
            diagram.AddRelation(
                new Composition(
                    diagram.Types.First(x => x.Name == source),
                    diagram.Types.First(x => x.Name == target),
                    Cardinality.One,
                    Cardinality.Multiple));
        }        
    }
}
